﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	WebContext.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Web.Caching;
using Microsoft.SharePoint;
using Smart.Common.Entities;

namespace Smart.Common
{
	/// <summary>
	/// SharePoint Web Scope
	/// </summary>
	[DebuggerDisplay("WebContext Name={Name}")]
	public class WebEntityScope : EntityScope, IWebEntityScope
	{
		/// <summary>
		/// Returns scope context for current web (all list, no business layer)
		/// </summary>
		public static WebEntityScope CurrentWeb
		{
			get { return GetScope(SPH.GetWeb()); }
		}

		/// <summary>
		/// Checks whether scope is accessible from current context
		/// </summary>
		public static bool IsScopeAccessible(string scopeName)
		{
			var services = SPServices.Current;

			var scopeSettings = GetScopeSettings(services, scopeName);

			if (scopeSettings == null || scopeSettings.Length <= 0)
				return false;

			var scopeTypeName = scopeSettings[0];

			if (scopeTypeName.IsNullOrEmpty())
				return false;

			var urlMask = scopeSettings[1];

			var url = SPH.FindWebUrlMatch(urlMask);

			return !url.Contains("*");
		}

		/// <summary>
		/// Web url
		/// </summary>
		public string WebUrl { get; protected set; }

		/// <summary>
		/// Disposable SPWeb
		/// </summary>
		public ManagedDisposable<SPWeb> Disposable { get; set; }

		/// <summary>
		/// Return whether this class is web enclosure
		/// </summary>
		public bool IsWebEnclosure { get; set; }

		/// <summary>
		/// SharePoint Web, associated with current scope
		/// </summary>
		public SPWeb Web
		{
			get { return Disposable; }
		}

		/// <summary>
		/// Web root folder
		/// </summary>
		public SPFolder Folder
		{
			get { return Web.RootFolder; }
		}

		/// <summary>
		/// SharePoint lists
		/// </summary>
		public HashSet<string> Lists { get; set; }

		/// <summary>
		/// Initialize virtual scope for Web (all list, no business layer)
		/// </summary>
		public static WebEntityScope GetScope(SPWeb web)
		{
			SPServices.Current.Load();

			var scope = new WebEntityScope(web);

			scope.InitScope(null, null);

			return scope;
		}

		/// <summary>
		/// Initialize SharePoint Web Scope Context
		/// </summary>
		public WebEntityScope()
		{
			Lists = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		}

		/// <summary>
		/// Initialize virtual scope for Web. Internal use only.
		/// </summary>
		protected WebEntityScope(SPWeb web)
			: this()
		{
			Disposable = web;
		}

		protected override IListService<TEntityType> GetListService<TEntityType>(MetaList list)
		{
			return new WebListService<TEntityType>(this, list.Name, WebEntityMapper.Current, list);
		}

		protected override IEntityFacade<TEntityType> CreateFacade<TEntityType>(string facadeName, IListService<TEntityType> service)
		{
			return new WebEntityFacade<TEntityType>(facadeName, FacadeMapping.SafeGet(facadeName), service);
		}

		protected override MetaScope LoadMetaScope()
		{
			return MetaCtx.LoadWebScope(Web, Lists, FacadeMapping, IsWebEnclosure);
		}

		public override object GetSource(MetaList list)
		{
			H.LT("WebContext.GetSource {0}", list.Name);

			return MetaCtx.List(Web, list.ID);
		}

		protected virtual SPWeb ParseSettings(out bool webShouldBeDisposed, params string[] settings)
		{
			var urlMask = settings[1];

			if (urlMask.IsNullOrEmpty())
				throw new NotSupportedException();

			var web = FindWebMatch(urlMask, out webShouldBeDisposed);

			if (settings.Length < 3)
				return web;

			for (var I = 2; I < settings.Length; I++)
			{
				var listInfo = settings[I];

				if (listInfo.IsNullOrEmpty())
					continue;

				var listSettings = listInfo.Split('#').ToList();

				for (var i = 0; i < listSettings.Count; i++)
				{
					var sv = listSettings[i];

					if (!sv.IsNullOrEmpty())
						listSettings[i] = sv.Trim();
				}

				// Empty line?
				if (listSettings.Count < 1 || (listSettings.Count == 1 && listSettings[0].IsNullOrEmpty()))
					continue;

				// Only facade defined?
				if (listSettings.Count == 1)
					listSettings.Add(listSettings[0]);

				if (listSettings.Count < 6)
					listSettings.AddRange(new string[6 - listSettings.Count]);

				var listFacade = listSettings[0];
				var listUrl = listSettings[1];

				if (listFacade.IsNullOrEmpty()) listFacade = listUrl;
				if (listUrl.IsNullOrEmpty()) listUrl = listFacade;

				if (H.IsAnyNullOrEmpty(listFacade, listUrl))
					continue;

				var cacheUserDependent = (listSettings[2] ?? string.Empty).ToLower().Replace("userdependent", "true").SToBool(false);
				var cacheAbsoluteTimeout = listSettings[3].SToTS(TimeSpan.MinValue);
				var cacheSlidingExpiration = listSettings[4].SToTS(TimeSpan.MinValue);
				var cachePriority = listSettings[5].SToE(CacheItemPriority.BelowNormal);

				Lists.Add(listUrl);

				FacadeMapping.Add(listFacade,
								  new FacadeMappingInfo(listFacade, listUrl)
									{
										CacheUserDependent = cacheUserDependent,
										CacheAbsoluteTimeout = cacheAbsoluteTimeout,
										CacheSlidingExpiration = cacheSlidingExpiration,
										CachePriority = cachePriority
									});
			}

			return web;
		}

		public static string GetCacheKey(string scopeName, string webUrl, string[] settings)
		{
			return
				scopeName.GetHashCodeX() + "-" +
				webUrl.GetHashCodeX() + "-" +
				settings.GetValuesHashCodeX();
		}

		public override void InitScope(string name, params string[] settings)
		{
			string scopeKey;

			if (name == null && settings == null)
			{
				IsWebEnclosure = true;

				// Virtual scope
				Name = Web.Name;
				WebUrl = SPH.NormalizeServerRelativeUrl(Web.ServerRelativeUrl);

				if (Name.IsNullOrEmpty())
					Name = "/";

				scopeKey = MetaCtx.GetWebScopeName(Web);
			}
			else if (name != null && settings != null && settings.Length >= 2)
			{
				IsWebEnclosure = false;

				// Regular scope (with name and url)
				Name = name;

				WebUrl = FindWebUrl(settings[1]);

				if (WebUrl.Contains("*"))
					throw new InvalidOperationException();

				Disposable =
					new ManagedDisposable<SPWeb>(
						GetCacheKey(Name, WebUrl, settings),
						() =>
						{
							// Parse settings
							bool shouldBeDisposed;
							var web = ParseSettings(out shouldBeDisposed, settings);

							return new ManagedDisposableInfo
								{
									Keys = new[] { GetCacheKey(Name, WebUrl, settings) },
									Disposable = web,
									ShouldBeDisposed = shouldBeDisposed,
									Args = new object[] { WebUrl, Lists, FacadeMapping }
								};
						});

				WebUrl = (string)Disposable.Info.Args[0];
				Lists = (HashSet<string>)Disposable.Info.Args[1];
				FacadeMapping = (Dictionary<string, FacadeMappingInfo>)Disposable.Info.Args[2];

				scopeKey = Name + "-" + GetCacheKey(Name, WebUrl, settings);
			}
			else
			{
				throw new ConfigurationErrorsException("scopeSettings");
			}

			Mapper = WebEntityMapper.Current;

			if (Lists.Count <= 0)
			{
				foreach (var listUrl in from SPList list in Web.Lists where !list.IsSystem() select list.GetUrl())
				{
					Lists.Add(listUrl);

					FacadeMapping.Add(listUrl, new FacadeMappingInfo(listUrl, listUrl));
				}
			}

			Meta = MetaCtx.GetScope(scopeKey, tmp => LoadMetaScope());
		}

		/// <summary>
		/// Return web scope settings header
		/// </summary>
		protected virtual string GetDefaultSettingsHeader(string scopeTypeName, string urlMask)
		{
			return
				string.Format(scopeTypeName + "\r\n" + urlMask);
		}

		/// <summary>
		/// Return default scope settings
		/// </summary>
		protected override string GetDefaultSettings(string scopeTypeName, string scopeName)
		{
			return
				GetDefaultSettingsHeader(scopeTypeName, "/");
		}

		/// <summary>
		/// Find Web url by <paramref name="urlMask"/>
		/// </summary>
		protected virtual string FindWebUrl(string urlMask)
		{
			return
				!urlMask.Contains("*")
					? SPH.NormalizeServerRelativeUrl(urlMask)
					: SPH.FindWebUrlMatch(urlMask);
		}

		/// <summary>
		/// Detect Web by <paramref name="urlMask"/>
		/// </summary>
		protected virtual SPWeb FindWebMatch(string urlMask, out bool shouldBeDisposed)
		{
			H.LT("WebContext.FindWeb {0}", urlMask);

			if (urlMask == "/")
			{
				shouldBeDisposed = false;

				return SPH.GetRootWeb();
			}

			return SPH.FindWebMatch(urlMask, out shouldBeDisposed);
		}

		protected override void DisposeManaged()
		{
			if (Disposable != null)
			{
				Disposable.Dispose();
				Disposable = null;
			}

			base.DisposeManaged();
		}
	}
}